using System;
using System.ServiceModel;
using System.ServiceModel.Channels;

namespace Albatross.WCF.Extenions.ServiceModel.Channels
{
    internal sealed class CompressionChannelFactory<TChannelShape> : ChannelFactoryBase<TChannelShape>
    {
        private readonly IChannelFactory<TChannelShape> _innerFactory;
        private readonly CompressionLevel _compressionLevel;
        private readonly CompressionAlgorithm _compressionAlgorithm;
        
        internal CompressionChannelFactory(CompressionBindingElement bindingElement, BindingContext context)
            : base(context.Binding)
        {
            _compressionLevel = bindingElement.Level;
            _compressionAlgorithm = bindingElement.Algorithm;
            _innerFactory = context.BuildInnerChannelFactory<TChannelShape>();
        }

        public CompressionLevel Level
        {
            get { return _compressionLevel; }
        }

        public CompressionAlgorithm Algorithm
        {
            get { return _compressionAlgorithm; }
        }

        private TChannelShape SetChannel(TChannelShape innerChannel)
        {
            if (innerChannel == null)
            {
                throw new ArgumentNullException("innerChannel");
            }

            if (typeof (TChannelShape) == typeof (IOutputChannel))
            {
                return
                    (TChannelShape)
                    (object) new CompressionOutputChannel<IOutputChannel>(this, (IOutputChannel) innerChannel);
            }
            if (typeof (TChannelShape) == typeof (IRequestChannel))
            {
                return (TChannelShape) (object) new CompressionRequestChannel<IRequestChannel>(this, (IRequestChannel) innerChannel);
            }
            if (typeof (TChannelShape) == typeof (IOutputSessionChannel))
            {
                return
                    (TChannelShape)
                    (object) new CompressionOutputSessionChannel(this, (IOutputSessionChannel) innerChannel);
            }
            if (typeof (TChannelShape) == typeof (IRequestSessionChannel))
            {
                return
                    (TChannelShape)
                    (object) new CompressionRequestSessionChannel(this, (IRequestSessionChannel) innerChannel);
            }
            if (typeof(TChannelShape) == typeof(IDuplexChannel))
            {
                return
                    (TChannelShape)
                    (object)new CompressionDuplexChannel<IDuplexChannel>(this, (IDuplexChannel)innerChannel);
            }
            if (typeof(TChannelShape) == typeof(IDuplexSessionChannel))
            {
                return
                    (TChannelShape)
                    (object)new CompressionDuplexSessionChannel(this, (IDuplexSessionChannel)innerChannel);
            }

            throw new NotSupportedException(String.Format("Unsupported channel type {0} passed to channel factory.", typeof(TChannelShape)));
        }

        protected override TChannelShape OnCreateChannel(EndpointAddress address, Uri via)
        {
            TChannelShape innerChannel = _innerFactory.CreateChannel(address, via);

            return SetChannel(innerChannel);
        }

        protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return _innerFactory.BeginOpen(timeout, callback, state);
        }

        protected override void OnEndOpen(IAsyncResult result)
        {
            _innerFactory.EndOpen(result);
        }

        protected override void OnOpen(TimeSpan timeout)
        {
            _innerFactory.Open(timeout);
        }

        public override T GetProperty<T>()
        {
            return _innerFactory.GetProperty<T>();
        }
    }
}